package assembler;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Assembler
{
	// Places to store the assembly instructions from the file and the
	// symbol table.
	private ArrayList<String> assemblyInstructions;
	private Map<String, Integer> symbolTable;
	private ArrayList<String> codeListingText;
	private ArrayList<Integer[]> codeListing;
	private ArrayList<String> codeComments;

	// Stores the error messages.
	private ArrayList<String> errorMessages;

	// Stores the current program counter used in creating the symbol
	// table.
	private int programCounter;
	private int programCounterNext;
	private int programCounterPrevious;

	// If this != null, then use this value instead of the programCounter
	// value. This is for when the program counter is a label which cannot
	// be stored in an int.
	private String programCounterLabel;

	// Have we seen .end yet?
	private boolean endOfFile;

	// Assembler instruction compliance settings.
	public static final int BL_COMPLIANCE = 1;
	public static final int EXT_COMPLIANCE = 2;
	private boolean BL_INSTR;
	private boolean EXT_INSTR;

	// The number formats for the parseNumber method.
	public static final int SIGNEXTEND = 1;
	public static final int SIGNMAGNITUDE = 2;
	public static final int UNSIGNED = 3;

	/**
	 * Initialize the Instructions class with an ArrayList to store the
	 * assembly instructions from the input file to, and a map to story
	 * the symbol table in.
	 */
	public Assembler()
	{
		assemblyInstructions = null;
		symbolTable = null;
		codeListing = null;
		codeListingText = null;
		errorMessages = new ArrayList<String>();
		codeComments = null;

		programCounter = 0;
		programCounterNext = 0;
		programCounterPrevious = 0;
		programCounterLabel = null;

		if (programCounterPrevious == 0)
		{
			//This makes the warning go away!
		}

		endOfFile = false;

		BL_INSTR = true;
		EXT_INSTR = true;
	}

	/**
	 * Load an .ASM assembly file.
	 *
	 * @param file A file of the assembly file to load.
	 * @return	Returns true if the load was successful, otherwise false.
	 */
	public boolean loadAssemblyFile(File file)
	{
		if (file == null)
			return false;

		Scanner scanner = null;
		Pattern commentsRegEx = Pattern.compile("^.+?;\\s*(.+?)$");

		// Reset the symbolTable, codeListing, assemblyInstructions, and comments.
		symbolTable = null;
		codeListing = null;
		codeListingText = null;
		if (assemblyInstructions == null)
		{
			assemblyInstructions = new ArrayList<String>();
			codeComments = new ArrayList<String>();
		}
		else
		{
			assemblyInstructions.clear();
			codeComments.clear();
		}

		try
		{
			scanner = new Scanner(file);
		}
		catch(FileNotFoundException fnf)
		{
			errorMessages.add("Unable to open file: " + file.getAbsolutePath() + "");
			return false;
		}

		while(scanner.hasNextLine())
		{
			String line = scanner.nextLine();

			assemblyInstructions.add(trim(line));

			// Add the comments to the comments array list for code listing output.
			Matcher commentsMatcher = commentsRegEx.matcher(line);
			if (commentsMatcher.matches())
				codeComments.add(commentsMatcher.group(1));
			else
				codeComments.add("");
		}

		scanner.close();

		return true;
	}

	/**
	 * Parse the assembly file and create the symbol table if it does not exist.
	 * If the table does exist, returns it.
	 *
	 * @return	The loaded file's symbol table. If no file loaded, returns null.
	 */
	public Map<String, Integer> getSymbolTable()
	{
		// If assemblyInstructions == null, than no file has been loaded.
		if (assemblyInstructions == null)
			return null;

		// The symbol table might already be made.
		if (symbolTable != null)
			return symbolTable;

		codeListing = null;
		codeListingText = null;

		// Create a temporary symbol table that is able to store labels
		// instead of just integers for the addresses.
		Map<String, Object> ST = new TreeMap<String, Object>();

		Pattern regEx = Pattern.compile("^(.+?):.*$");
		Pattern labelRegEx = Pattern.compile("^[A-Z][A-Z0-9_]*?$");
		Matcher matcher = null;

		// Reset the fields to their defaults.
		endOfFile = false;
		programCounter = 0;
		programCounterNext = 0;
		programCounterPrevious = 0;
		programCounterLabel = null;
		int errorCount = errorMessages.size();

		int instructionCount = assemblyInstructions.size();
		for (int i = 0; i < instructionCount; i++)
		{
			String line = assemblyInstructions.get(i);
			if (line.equals("")) continue;
			int lineNumber = i + 1;

			if (endOfFile == true)
			{
				errorMessages.add("Line " + lineNumber + ": Cannot have additional statements past .end.");
				break;
			}

			programCounterPrevious = programCounter;
			programCounter = programCounterNext;
			int instructions = parseInstruction(line, lineNumber).split("\n").length;

			for (int j = 0; j < instructions; j++)
			{
				matcher = regEx.matcher(line);
				if (matcher.matches())
				{
					// Here we have to check if the label is valid. The label must start
					// with a capital letter and then it can be followed by 0 or more capital
					// letters or numbers.
					String label = matcher.group(1);
					if (!labelRegEx.matcher(label).matches())
						errorMessages.add("Line " + lineNumber + ": Invalid label, " + label + ".");
					// Here we check if the label is duplicated.
					if (ST.get(label) != null)
						errorMessages.add("Line " + lineNumber + ": Duplicate label, " + label + ".");


					if (programCounterLabel == null)
					{
						ST.put(label, programCounter);
					}
					else
					{
						Object address = ST.get(programCounterLabel);
						if (address != null && address instanceof Integer)
							ST.put(label, (Integer) address);
						else
							ST.put(label, programCounterLabel);

						if (j == instructions - 1)
							programCounterLabel = null;
					}
				}
				else
				{
					if (getNthWord(line, 1).equals(".equ"))
						errorMessages.add("Line " + lineNumber + ": .equ lines must contain a label.");
				}

				// Increment the programCounter on all except the last loop.
				if (j != instructions - 1)
					programCounterPrevious = programCounter++;
			}
		}

		// This isn't pretty, but this loops through ST multiple times in an
		// attempt to associate addresses to those labels that reference a
		// second label. If this can't be done in ten passes, there there is
		// probably a recursive relationship between to labels, i.e. L2 references
		// L3 and L3 references L2.
		boolean allAddresses = true;
		int loopCounter = 0;
		for (loopCounter = 0; loopCounter < 10; loopCounter++);
		{
			ArrayList<String> problemLabels = new ArrayList<String>();
			allAddresses = true;
			Iterator<String> it = ST.keySet().iterator();
			while (it.hasNext())
			{
				String label = it.next();
				Object address = ST.get(label);
				if (address instanceof String)
				{
					allAddresses = false;
					problemLabels.add(label);
				}
			}

			for (int i = 0; i < problemLabels.size(); i++)
			{
				String label = problemLabels.get(i);
				Object address = ST.get(label);

				ST.remove(label);

				it = ST.keySet().iterator();
				while (it.hasNext())
				{
					String STlabel = it.next();
					Object STaddress = ST.get(STlabel);

					if (STlabel.equals(address) && STaddress != null)
					{
						address = STaddress;
						break;
					}
				}

				ST.put(label, address);
			}

			// If ST doesn't contain any labels, make it exit.
			if (allAddresses == true)
				loopCounter = 15;
		}

		symbolTable = new TreeMap<String, Integer>();

		Iterator<String> it = ST.keySet().iterator();
		while (it.hasNext())
		{
			String label = it.next();
			Object address = ST.get(label);
			if (address instanceof Integer)
				symbolTable.put(label, (Integer) address);
			else
			{
				errorMessages.add("Unable to obtain address for label " + label + ".");
			    errorMessages.add("   Possibly because of a recursive relationship between it and " + address + ".");
			}
		}

		// If all of the addresses weren't integers, then this file cannot
		// be loaded, reset everything.
		if (!allAddresses)
		{
			symbolTable = null;
			assemblyInstructions = null;
			codeListing = null;
			codeListingText = null;
		}

		// If the current amount of errors is greater than at the beginning of the
		// method, then there were errors, so we want to return null.
		if (errorMessages.size() > errorCount)
		{
			symbolTable = null;
			codeListing = null;
			codeListingText = null;
		}

		return symbolTable;
	}

	/**
	 * Returns the code listing with addresses and HEX output. If the symbol
	 * table has not been created yet, returns null.
	 *
	 * @return	The code listing or null if the symbol table has not been created.
	 */
	public ArrayList<String> getCodeListing()
	{
		return getCodeListing(false);
	}

	/**
	 * Returns the code listing with addresses and HEX output. If the symbol
	 * table has not been created yet, returns null. If comments is false,
	 * it does not print the comments after the assembly code, if it is true,
	 * it does print the comments.
	 *
	 * @param comments	Whether or not to print the comments after the assembly code.
	 * @return	The code listing or null if the symbol table has not been created.
	 */
	public ArrayList<String> getCodeListing(boolean comments)
	{
		// Is the listing already created?
		if (codeListingText != null)
			return codeListingText;

		// Is the symbol table created and do we have a file loaded?
		if (symbolTable == null || assemblyInstructions == null)
			return null;

		// If comments are wanted, but the comments array list is null,
		// we cannot fulfill the request.
		if (comments == true && codeComments == null)
			comments = false;

		// Okay, a code listing isn't created, but a symbol table is.
		codeListing = new ArrayList<Integer[]>();
		codeListingText = new ArrayList<String>();

		// Reset the fields to their defaults.
		endOfFile = false;
		programCounter = 0;
		programCounterNext = 0;
		programCounterPrevious = 0;
		programCounterLabel = null;

		int instructionCount = assemblyInstructions.size();
		for (int i = 0; i < instructionCount; i++)
		{
			String line = assemblyInstructions.get(i);
			if (line.equals("")) continue;
			int lineNumber = i + 1;

			if (endOfFile == true)
			{
				errorMessages.add("Line " + lineNumber + ": Cannot have additional statements past .end.");
				break;
			}

			programCounterPrevious = programCounter;
			programCounter = programCounterNext;
			String[] binary = parseInstruction(line, lineNumber).split("\n");

			line = line.replaceAll("\\s+", "\t");
			if (line.indexOf(":") < 0)
				line = "\t" + line;

			for (int j = 0; j < binary.length; j++)
			{
				String code = binary[j];

				if (comments && !codeComments.get(i).equals(""))
					line += " ; " + codeComments.get(i);

				if (code.equals(""))
				{
					if (j == 0)
						codeListingText.add("\t" + code + "\t" + line);
					else
						codeListingText.add("");
				}
				else
				{
					Integer[] codeListingArray = {programCounter, parseNumber("0b" + code, UNSIGNED)};
					codeListing.add(codeListingArray);

					if (j == 0)
						codeListingText.add(convertToHexString(programCounter, 4) + ": " + convertToHexString(parseNumber("0b" + code, UNSIGNED), 4) + "\t" + line);
					else
						codeListingText.add(convertToHexString(programCounter, 4) + ": " + convertToHexString(parseNumber("0b" + code, UNSIGNED) ,4));
				}

				programCounterPrevious = programCounter++;
			}
		}

		if (endOfFile == false)
			errorMessages.add("There is no .end directive in the assembly code file.");

		return codeListingText;

	}

	/**
	 * Parses the instruction parameter and outputs the binary code. Also
	 * updates the PC parameter.
	 *
	 * @param instr	The instruction to be parsed.
	 * @param lineNumber	The line number of the instruction for error purposes.
	 * @return	Returns the binary output as a string with 0's and 1's.
	 */
	private String parseInstruction(String instr, int lineNumber)
	{
		String binary = "";
		String opcode = getNthWord(instr, 1);
		String arg1 = getNthWord(instr, 2);
		String arg2 = getNthWord(instr, 3);

		if (arg1.length() == 0) arg1 = " ";
		if (arg2.length() == 0) arg2 = " ";

		// BASE-LINE INSTRUCTIONS
		if (opcode.equals("add") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for ADD.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst register for ADD.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "0101" + Rsrc;
		}
		else if (opcode.equals("addi") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for ADDI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for ADDI.");
				errors = true;
			}

			if (!errors)
				binary = "0101" + Rdst + Imm;
		}
		else if (opcode.equals("sub") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for SUB.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Raddr for SUB.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "1001" + Rsrc;
		}
		else if (opcode.equals("subi") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for SUBI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for SUBI.");
				errors = true;
			}

			if (!errors)
				binary = "1001" + Rdst + Imm;
		}
		else if (opcode.equals("cmp") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for CMP.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst register for CMP.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "1011" + Rsrc;
		}
		else if (opcode.equals("cmpi") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for CMPI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for CMPI.");
				errors = true;
			}

			if (!errors)
				binary = "1011" + Rdst + Imm;
		}
		else if (opcode.equals("and") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for AND.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst register for AND.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "0001" + Rsrc;
		}
		else if (opcode.equals("andi") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for ANDI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for ANDI.");
				errors = true;
			}

			if (!errors)
				binary = "0001" + Rdst + Imm;
		}
		else if (opcode.equals("or") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for OR.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst register for OR.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "0010" + Rsrc;
		}
		else if (opcode.equals("ori") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for ORI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for ORI.");
				errors = true;
			}

			if (!errors)
				binary = "0010" + Rdst + Imm;
		}
		else if (opcode.equals("xor") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for XOR.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst register for XOR.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "0011" + Rsrc;
		}
		else if (opcode.equals("xori") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for XORI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for XORI.");
				errors = true;
			}

			if (!errors)
				binary = "0011" + Rdst + Imm;
		}
		else if (opcode.equals("mov") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for MOV.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst register for MOV.");
				errors = true;
			}

			if (!errors)
				binary = "0000" + Rdst + "1101" + Rsrc;
		}
		else if (opcode.equals("movi") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for MOVI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for MOVI.");
				errors = true;
			}

			if (!errors)
				binary = "1101" + Rdst + Imm;
		}
		else if (opcode.equals("lsh") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Ramount = getLabelRegister(arg1, 4, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Ramount == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Ramount for LSH.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for LSH.");
				errors = true;
			}

			if (!errors)
				binary = "1000" + Rdst + "0100" + Ramount;
		}
		else if (opcode.equals("lshi") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = null;
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);
			String Sign = "0";

			Integer number = parseNumber(arg1, SIGNMAGNITUDE);
			if (number != null)
			{
				if (number < 0)
					Sign = "1";

				if (number >= -15 && number <= 15)
				{
					if (Sign.equals("1"))
						Imm = Integer.toBinaryString(-1 * number);
					else
						Imm = Integer.toBinaryString(number);

					while (Imm.length() < 4)
						Imm = '0' + Imm;
				}
			}

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Imm for LSHI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for LSHI.");
				errors = true;
			}

			if (!errors)
				binary = "1000" + Rdst + "000" + Sign + Imm;
		}
		else if (opcode.equals("lui") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Imm = getLabelRegister(arg1, 8, UNSIGNED);
			String Rdst = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) == 'r' || Imm == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid immediate address for LUI.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rdst == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdst for LUI.");
				errors = true;
			}

			if (!errors)
				binary = "1111" + Rdst + Imm;
		}
		else if (opcode.equals("load") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rdest = getLabelRegister(arg1, 4, UNSIGNED);
			String Raddr = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rdest == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rdest register for LOAD.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Raddr == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Raddr for LOAD.");
				errors = true;
			}

			if (!errors)
				binary = "0100" + Rdest + "0000" + Raddr;
		}
		else if (opcode.equals("stor") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rsrc = getLabelRegister(arg1, 4, UNSIGNED);
			String Raddr = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rsrc == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rsrc register for STOR.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Raddr == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Raddr for STOR.");
				errors = true;
			}

			if (!errors)
				binary = "0100" + Rsrc + "0100" + Raddr;
		}
		else if (opcode.equals("jal") && BL_INSTR)
		{
			incrementPC(1);
			if (symbolTable == null) return "";
			boolean errors = false;

			String Rlink = getLabelRegister(arg1, 4, UNSIGNED);
			String Rtarget = getLabelRegister(arg2, 4, UNSIGNED);

			if (arg1.charAt(0) != 'r' || Rlink == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rlink register for JAL.");
				errors = true;
			}
			if (arg2.charAt(0) != 'r' || Rtarget == null)
			{
				errorMessages.add("Line " + lineNumber + ": Invalid Rtarget register for JAL.");
				errors = true;
			}

			if (!errors)
				binary = "0100" + Rlink + "1000" + Rtarget;
		}
		else if (opcode.equals("Bcond") && BL_INSTR)
		{
			incrementPC(1);
			// BCond + cond + register
			String cond = "";
			String Imm = getLabelRegister(arg1, 8, UNSIGNED);

			if (arg1.equals("EQ"))
			{
				cond = "0000";
			}
			else if (arg1.equals("NE"))
			{
				cond = "0001";
			}
			else if (arg1.equals("GE"))
			{
				cond = "1101";
			}
			else if (arg1.equals("CS"))
			{
				cond = "0010";
			}
			else if (arg1.equals("CC"))
			{
				cond = "0011";
			}
			else if (arg1.equals("HI"))
			{
				cond = "0100";
			}
			else if (arg1.equals("LS"))
			{
				cond = "0101";
			}
			else if (arg1.equals("LO"))
			{
				cond = "1010";
			}
			else if (arg1.equals("HS"))
			{
				cond = "1011";
			}
			else if (arg1.equals("GT"))
			{
				cond = "0110";
			}
			else if (arg1.equals("LE"))
			{
				cond = "0111";
			}
			else if (arg1.equals("FS"))
			{
				cond = "1000";
			}
			else if (arg1.equals("FC"))
			{
				cond = "1001";
			}
			else if (arg1.equals("LT"))
			{
				cond = "1100";
			}
			else if (arg1.equals("UC"))
			{
				cond = "1111";
			}
			else
				errorMessages.add("Line " + lineNumber + ": not a valid COND value.");

			binary = "1100" + cond + Imm;

		}
		else if (opcode.equals("Jcond") && BL_INSTR)
		{
			incrementPC(1);
			// JCondOpCode1 + cond + JCondOpCode2 + RTarget
			String RTarget = getLabelRegister(arg2, 4, UNSIGNED);
			String cond = "";

			if (arg1.equals("EQ"))
			{
				cond = "0000";
			}
			else if (arg1.equals("NE"))
			{
				cond = "0001";
			}
			else if (arg1.equals("GE"))
			{
				cond = "1101";
			}
			else if (arg1.equals("CS"))
			{
				cond = "0010";
			}
			else if (arg1.equals("CC"))
			{
				cond = "0011";
			}
			else if (arg1.equals("HI"))
			{
				cond = "0100";
			}
			else if (arg1.equals("LS"))
			{
				cond = "0101";
			}
			else if (arg1.equals("LO"))
			{
				cond = "1010";
			}
			else if (arg1.equals("HS"))
			{
				cond = "1011";
			}
			else if (arg1.equals("GT"))
			{
				cond = "0110";
			}
			else if (arg1.equals("LE"))
			{
				cond = "0111";
			}
			else if (arg1.equals("FS"))
			{
				cond = "1000";
			}
			else if (arg1.equals("FC"))
			{
				cond = "1001";
			}
			else if (arg1.equals("LT"))
			{
				cond = "1100";
			}
			else if (arg1.equals("UC"))
			{
				cond = "1111";
			}
			else
				errorMessages.add("Line " + lineNumber + ": not a valid COND value.");

			// JCondOpCode1 + cond + JCondOpCode2 + RTarget
			binary = "0100" + cond + "1100" + RTarget;

		}
		else if (opcode.equals(".org") && BL_INSTR)
		{
			Integer integer = parseNumber(arg1, UNSIGNED);
			if (integer == null)
			{
				if (isLabel(arg1))
					programCounterLabel = arg1;
				else
					errorMessages.add("Line " + lineNumber + ": Following .org, there must be a number.");
			}
			else
			{
				if (integer > 0xFFFF || integer < 0)
					errorMessages.add("Line " + lineNumber + ": The memory address must be between 0 and 0xFFFF, inclusive.");
				programCounter = programCounterNext = integer;
			}
		}
		else if (opcode.equals(".word") && BL_INSTR)
		{
			incrementPC(1);

			if (isLabel(arg1))
				arg1 = "0b" + getLabelRegister(arg1, 16, UNSIGNED);

			Integer integer = parseNumber(arg1, UNSIGNED);
			if (integer == null)
				errorMessages.add("Line " + lineNumber + ": The memory address specified could not be parsed.");
			else if (integer > 0xFFFF || integer < 0)
				errorMessages.add("Line " + lineNumber + ": The memory address must be between 0 and 0xFFFF, inclusive.");
			else
				binary = Integer.toBinaryString(integer);
		}
		else if (opcode.equals(".end") && BL_INSTR)
		{
			endOfFile = true;

			binary = "0000" + "0000" + "0000" + "0000";
		}

		// EXTENDED INSTRUCTIONS
		// (If any, add here.)

		// Unknown opcode.
		else
		{
			errorMessages.add("Unknown keyword, " + opcode + ", used on line " + lineNumber + ".");
		}

		return binary;
	}

	/**
     * Creates the MCS output file from the loaded assembly program. If
     * there is no program loaded or the code listing has not been generated
     * yet, the method returns null.
     *
     * @return  The MCS file, or null if there is no file loaded or the code
     * 			listing has not been generated.
     */
    public ArrayList<String> getMCSOutput()
    {
        // Is the listing created?
        if (codeListing == null)
            return null;

        // Create an ArrayList to store the outputted strings.
        ArrayList<String> output = new ArrayList<String>();

        Iterator<Integer[]> it = codeListing.iterator();
        while (it.hasNext())
        {
            // line[0] = the address of the instruction
            // line[1] = the instruction as a hex number
        	Integer[] line = it.next();
            // Multiply by 2 because the code is word addressed and the MCS output is
        	// byte addressed.
        	int address = 2*line[0];
            int instruction = line[1];

            // If there is a memory address extension inplace, what is it?
            String currentMemoryExtension = null;

            // The MCS record is of the following format:
            // :<nbytes><startaddr><recordtype><by><by>...<by><checksum>
            String MCSrecord = ":";
            String instructionHex = convertToHexString(instruction, 4);
            {
            	int numberOfBytes = instructionHex.length() / 2;
                String numberOfBytesHex = Integer.toHexString(numberOfBytes);
                if (numberOfBytesHex.length() < 2)
                	MCSrecord +=  "0" + numberOfBytesHex;
                else
                	MCSrecord += numberOfBytesHex.substring(numberOfBytesHex.length() - 2);
            }
            String addressHex = convertToHexString(address, 4);
            if (addressHex.length() == 4 && currentMemoryExtension != null)
            {
            	// We are going from addresses greater than 2 bytes into
            	// 2 byte addresses, remove the address extension record.

            	currentMemoryExtension = null;
            	output.add(":020000040000FA");
            }
            else if (addressHex.length() > 4)
            {
            	if (addressHex.length() > 8)
            	{
            		errorMessages.add("ERROR: Unable to add instructions with addresses greater than 4 bytes.");
            		return null;
            	}

            	// Make the string 8 characters long if it isn't already.
            	while (addressHex.length() < 8)
            		addressHex = '0' + addressHex;

            	// No address extension record.
            	if (currentMemoryExtension == null)
            	{
            		currentMemoryExtension = addressHex.substring(0, 4);
            		addressHex = addressHex.substring(4);

            		String extensionRecord = ":02000004" + currentMemoryExtension;
            		extensionRecord += calculateChecksum(extensionRecord);
            		output.add(extensionRecord);
            	}
            	else
            	{
            		String newMemoryExtension = addressHex.substring(0, 4);
            		addressHex = addressHex.substring(4);
            		if (!currentMemoryExtension.equals(newMemoryExtension))
            		{
            			currentMemoryExtension = newMemoryExtension;

                		String extensionRecord = ":02000004" + currentMemoryExtension;
                		extensionRecord += calculateChecksum(extensionRecord);
                		output.add(extensionRecord);
            		}
            	}
            }
            MCSrecord += addressHex;
            MCSrecord += "00"; // data-record
            MCSrecord += instructionHex;
            MCSrecord += calculateChecksum(MCSrecord);

            output.add(MCSrecord.toUpperCase());
        }
        output.add(":00000001FF"); // end-of-file record

        return output;
    }

	/**
     * Creates the XES output file from the loaded assembly program. If
     * there is no program loaded or the code listing has not been generated
     * yet, the method returns null.
     *
     * @return  The XES file, or null if there is no file loaded or the code
     * 			listing has not been generated.
     */
    public ArrayList<String> getXESOutput()
    {
        // Is the listing created?
        if (codeListing == null)
            return null;

        // Create an ArrayList to store the outputted strings.
        ArrayList<String> output = new ArrayList<String>();

        Iterator<Integer[]> it = codeListing.iterator();
        while (it.hasNext())
        {
            // line[0] = the address of the instruction
            // line[1] = the instruction as a hex number
        	Integer[] line = it.next();
            // Multiply by 2 because the code is word addressed and the MCS output is
        	// byte addressed.
        	int address = 2*line[0];
            int instruction = line[1];

            // The XES record is of the following format:
            // + <nbytes> <startaddr> <by> <by> ... <by>
            String XESrecord = "+ ";
            String instructionHex = convertToHexString(instruction, 4);
            {
            	int numberOfBytes = instructionHex.length() / 2;
                String numberOfBytesHex = Integer.toHexString(numberOfBytes);
                if (numberOfBytesHex.length() < 2)
                	XESrecord +=  "0" + numberOfBytesHex + " ";
                else
                	XESrecord += numberOfBytesHex.substring(numberOfBytesHex.length() - 2) + " ";
            }
            String addressHex = convertToHexString(address, 8);

            XESrecord += addressHex + " ";

            for (int i = 0; i < instructionHex.length(); i++)
            {
            	if (i % 2 == 0 && i != 0)
            		XESrecord += " ";
            	XESrecord += instructionHex.charAt(i);
            }

            output.add(XESrecord.toUpperCase());
        }

        return output;
    }

	/**
     * Creates the MIF output file from the loaded assembly program. If
     * there is no program loaded or the code listing has not been generated
     * yet, the method returns null.
     *
     * @return  The MIF file, or null if there is no file loaded or the code
     * 			listing has not been generated.
     */
    public ArrayList<String> getMIFOutput()
    {
        // Is the listing created?
        if (codeListing == null)
            return null;

        // Create an ArrayList to store the outputted strings.
        ArrayList<String> output = new ArrayList<String>();

        Iterator<Integer[]> it = codeListing.iterator();
        while (it.hasNext())
        {
            // line[0] = the address of the instruction
            // line[1] = the instruction as a hex number
        	Integer[] line = it.next();
            int instruction = line[1];

            // The MIF record is simply the binary string of
            // each instruction, one instruction per line.

            String MIFrecord = "";
            String instructionBinary = convertToBinaryString(instruction, 4);
            for (int i = 0; i < instructionBinary.length(); i++)
            {
            	if (i % 2 == 0 && i != 0)
            		MIFrecord += " ";
            	MIFrecord += instructionBinary.charAt(i);
            }

            output.add(MIFrecord.toUpperCase());
        }

        return output;
    }

    /**
     * The checksum is calculated by summing each of the bytes of the passed
     * in data string. The data string is in hex, so a byte is two characters.
     * The sum is then chopped to 8 bits and the two's complement is taken.
     *
     * @param data	The string of which to calculate the checksum on. Before processing,
     * 				all non hex characters will be removed from this string.
     * @return	Returns the checksum of the passed in String. If the string is empty
     * 			or if the string's length is not even, null is returned.
     */
    private String calculateChecksum(String data)
    {
    	// Strip out all non-hex characters.
    	data = data.replaceAll("[^A-Fa-f0-9]", "");

    	// Is the string an even length? If not, this isn't legal.
    	if (data.length() % 2 != 0 || data.length() < 1)
    		return null;

    	// Sum all of the bytes of the input.
        int sum = 0;
        for(int i = 0; i < data.length(); i += 2)
        {
        	String currentByte = data.substring(i, i+2);
        	sum += Integer.parseInt("000000" + currentByte, 16);
        }

        // Use only the first 8 bits, and take the two's complement in
        // order to calculate the checksum.
        sum = -1 * (sum & 0xFF);
        String hexString = Integer.toHexString(sum);
        if (hexString.length() < 2)
        	return "0" + hexString.toUpperCase();
        else
        	return hexString.substring(hexString.length() - 2).toUpperCase();
    }

	/**
	 * Gets all of the error messages that were generated since the last
	 * time this function was called.
	 *
	 * @return	The error messages.
	 */
	public ArrayList<String> getErrorMessages()
	{
		ArrayList<String> errors = new ArrayList<String>(errorMessages);
		errorMessages.clear();
		return errors;
	}

	/**
	 * Removes the whitespace around the line of code and also the
	 * comments, so it is ready for processing.
	 *
	 * @param input	The original line of code.
	 * @return	The processed line of code with no comments and unnessesary white space.
	 */
	private String trim(String input)
	{
		Pattern regEx = Pattern.compile("^\\s*(.*?)\\s*(?:;.*)?\\s*$");
		Matcher matcher = regEx.matcher(input);

		if (matcher.matches())
			return matcher.group(1).replaceAll("\\s+", " ");
		else
			return "";
	}

	/**
	 * Get's the Nth word from the input string. The input is run through the trim
	 * function and the label is removed if there is one before the String is
	 * processed. If the Nth word does not exist, an empty String is returned.
	 *
	 * @param input	The original line of code.
	 * @return	The Nth word of the input string.
	 */
	private String getNthWord(String input, int N)
	{
		if (input == null || N < 1)
			return "";

		// Trim the label out of the input string.
		input = trim(input).replaceAll("^.*?:\\s*", "");
		if (N == 1)
		{
			// Handle N == 1 seprately because the 1st and 2nd words are separated
			// by a space instead of a comma.
			Matcher matcher = Pattern.compile("^(.*?)\\s+.*$").matcher(input);
			if (matcher.matches())
				return matcher.group(1);
			else
				return input;
		}

		// Strip out the first word because that was handled above.
		String[] split = input.replaceAll("^.+?\\s+", "").split("\\s*,\\s*");
		if (N-2 < split.length)
			return split[N-2];
		else
			return "";
	}

	/**
	 * Returns true if the label parameter is a label in the currently loaded
	 * file. Otherwise returns false.
	 *
	 * @param label	The label to see if it is a label in the current file.
	 * @return	True if it is a label, otherwise false.
	 */
	private boolean isLabel(String label)
	{
		if (assemblyInstructions == null || label == null)
			return false;

		int instructionCount = assemblyInstructions.size();
		for (int i = 0; i < instructionCount; i++)
		{
			String line = assemblyInstructions.get(i);
			if (line.startsWith(label + ": "))
				return true;
		}

		return false;
	}

	/**
	 * Parse numbers from the assembly file. The numbers can be in decimal (both positive
	 * and negative), the numbers can be in hex (0xNUM), or the numbers can be in binary
	 * (0bNUM). If the number is in hex or binary, to input a negative number, you can't
	 * use -0xNUM or -0bNUM. The NUM is a two's complement form so the '-' before the
	 * 0x or 0b is not accepted.
	 *
	 * @param number	The string of the number to parse.
	 * @param numberFormat	The way in wish to extend the numbers: SIGNEXTEND,
	 * 						SIGNMAGNITUDE, or UNSIGNED.
	 * @return	An Integer which is the String number. If the number is not valid, returns
	 * 			null.
	 */
	public static Integer parseNumber(String number, int numberFormat)
	{
		if (number == null || number.equals(""))
			return null;

		try
		{
			if (number.startsWith("0x"))
			{
				// Only accept all uppercase letters as per specifications.
				number = number.substring(2);
				if (!number.equals(number.toUpperCase()))
					return null;
				// Convert the number to binary because the Integer parser doesn't
				// parse in two's complement.
				String binaryString = "0b";
				for (int i = 0; i < number.length(); i++)
				{
					int integer = Integer.parseInt("" + number.charAt(i), 16);
					String binaryIntString = Integer.toBinaryString(integer);
					// Put in the leading zeros, except for the first hex digit.
					if (i != 0)
						for (int j = binaryIntString.length(); j < 4; j++)
							binaryIntString = '0' + binaryIntString;
					binaryString += binaryIntString;
				}
				number = binaryString;
			}

			if (number.startsWith("0b"))
			{
				number = number.substring(2);
				// Sign extend or zero extend the bits.
				if (number.length() != 32)
				{
					char maxBit = '0';
					if (numberFormat == SIGNEXTEND)
					{
						maxBit = number.charAt(0);
					}
					else if (numberFormat == SIGNMAGNITUDE)
					{
						maxBit = number.charAt(0);
						number = number.substring(1);
						while (number.length() != 32)
							number = '0' + number;
						number = maxBit + number.substring(1);
					}

					while (number.length() != 32)
						number = maxBit + number;
				}

				// Loop through and make the integer through multiplying by powers.
				int result = 0;
				for (int i = 31; i >= 0; i--)
				{
					if (i == 0 && number.charAt(0) == '1' && numberFormat == SIGNEXTEND)
						result -= Math.pow(2, 31);
					else if (i == 0 && number.charAt(0) == '1' && numberFormat == SIGNMAGNITUDE)
						result *= -1;
					else if (number.charAt(i) == '1')
						result += Math.pow(2, 31 - i);
				}

				return result;
			}

			return Integer.parseInt(number, 10);
		}
		catch (NumberFormatException e)
		{
			return null;
		}
	}

	/**
	 * Converts an int to a String Hex representation. Adds the leading zeros and
	 * converts it to all uppercase so it is ready to be printed.
	 *
	 * @param number	The int to convert.
	 * @param multipleOf	The multiple of the number will be the length of the
	 * 						outputted as the string.
	 * @return	The String hex representation of the number.
	 */
	public static String convertToHexString(int number, int multipleOf)
	{
		String string = Integer.toHexString(number);

		while (string.length() % multipleOf != 0)
			string = "0" + string;

		return string.toUpperCase();
	}

	/**
	 * Converts an int to a String Binary representation. Adds the leading zeros
	 * so it is ready to be printed.
	 *
	 * @param number		The int to convert.
	 * @param multipleOf	The multiple of the number will be the length of the
	 * 						outputted as the string.
	 * @return	The String binary representation of the number.
	 */
	public static String convertToBinaryString(int number, int multipleOf)
	{
		String string = Integer.toBinaryString(number);

		while (string.length() % multipleOf != 0)
			string = "0" + string;

		return string.toUpperCase();
	}


	/**
	 * Increments the programCounterNext field by amount, and if programCounterNext
	 * is over 16 bits (0xFFFF), it wraps it back down to 0.
	 *
	 * @param amount	The amount of incrementation.
	 */
	private void incrementPC(int amount)
	{
		programCounterNext += amount;

		if (programCounterNext > 0xFFFF)
			programCounterNext =- 0xFFFF;
	}

	/**
	 * Returns a binary representation of the passed arg. If the arg is a label,
	 * it returns the address of that label and if it is a register, it does the
	 * same.
	 *
	 * @param arg	The arg to analyze.
	 * @param length	The length of the binary string to return.
	 * @param numberFormat	The way in wish to extend the numbers: SIGNEXTEND,
	 * 						SIGNMAGNITUDE, or UNSIGNED.
	 * @return	Returns a binary representation of the passed arg. If it is illegal,
	 * 			null is returned.
	 */
	private String getLabelRegister(String arg, int length, int numberFormat)
	{
		if (arg == null || length < 1 || length > 32)
			return null;

		String binary = "";

		// Is it a register?
		if (arg.equals("r0"))
			binary = "0";
		else if (arg.equals("r1"))
			binary = "1";
		else if (arg.equals("r2"))
			binary = "10";
		else if (arg.equals("r3"))
			binary = "11";
		else if (arg.equals("r4"))
			binary = "100";
		else if (arg.equals("r5"))
			binary = "101";
		else if (arg.equals("r6"))
			binary = "110";
		else if (arg.equals("r7"))
			binary = "111";
		else if (arg.equals("r8"))
			binary = "1000";
		else if (arg.equals("r9"))
			binary = "1001";
		else if (arg.equals("rA"))
			binary = "1010";
		else if (arg.equals("rB"))
			binary = "1011";
		else if (arg.equals("rC"))
			binary = "1100";
		else if (arg.equals("rD"))
			binary = "1101";
		else if (arg.equals("rE"))
			binary = "1110";
		else if (arg.equals("rF"))
			binary = "1111";

		// Is it a label?
		if (binary.equals("") && symbolTable != null)
		{
			Integer address = symbolTable.get(arg);

			if (address != null)
				binary = Integer.toBinaryString(address);
		}

		// If it isn't a label or register, it must be an immediate value.
		if (binary.equals(""))
		{
			Integer number = parseNumber(arg, numberFormat);
			if (number != null)
				binary = Integer.toBinaryString(number);
		}

		// It wasn't a register, label, or an immediate. It's nothing!
		if (binary.equals(""))
			return null;

		if (binary.length() < length)
		{
			char maxBit = '0';
			if (numberFormat == SIGNEXTEND)
				maxBit = binary.charAt(0);
			else if (numberFormat == SIGNMAGNITUDE)
			{
				maxBit = binary.charAt(0);
				binary = binary.substring(1);
				while (binary.length() != length)
					binary = '0' + binary;
				binary = maxBit + binary.substring(1);
			}

			while (binary.length() != length)
				binary = maxBit + binary;
		}
		else if (binary.length() > length)
		{
			char maxBit = binary.charAt(0);
			int startIndex = binary.length() - length;

			// Make sure all of the chars that we are going to drop are all
			// the same, the number does not fit in the allocated number of
			// bits. It is <= because the first bit we keep has to be the same
			// as the max bit.
			boolean allSameChars = true;
			for (int i = 0; i <= startIndex; i++)
				if (binary.charAt(i) != maxBit)
					allSameChars = false;

			if (allSameChars)
				return binary.substring(startIndex);
			else
				return null;
		}

		return binary;
	}

	/**
	 * Returns the compliance setting of the assembler. This determines what
	 * instructions the compiler will recognize. There are two settings,
	 * Extended and BaseLine.
	 *
	 * @return	Returns one of the static fields EXT,BL_COMPLIANCE.
	 */
	public int getCompliance()
	{
		if (EXT_INSTR && BL_INSTR)
			return EXT_COMPLIANCE;
		else if (!EXT_INSTR && BL_INSTR)
			return BL_COMPLIANCE;
		else
		{
			// If none of the above are true, set to extended compliance.
			EXT_INSTR = true;
			BL_INSTR = true;

			return EXT_COMPLIANCE;
		}
	}

	/**
	 * Sets the assembler compliance setting to the passed in compliance param.
	 *
	 * @param compliance	The compliance setting to be set.
	 */
	public void setCompliance(int compliance)
	{
		if (compliance == EXT_COMPLIANCE)
		{
			EXT_INSTR = true;
			BL_INSTR = true;
		}
		else if (compliance == BL_COMPLIANCE)
		{
			EXT_INSTR = false;
			BL_INSTR = true;
		}
	}

}